/*
 *  Copyright 2009 Advanced Industrial Science and Technology (AIST).
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*! \file
 *  \brief
 */
#pragma once

#include <thrust/pair.h>

#include <iostream>

namespace thrust {

namespace detail {
    
template <class VertexIterator, class OutEdgeIterator, class Graph>
class adj_list_edge_iterator {

private:
  typedef adj_list_edge_iterator self_t;
  typedef typename OutEdgeIterator::called_space called_space;
  
public:
  VertexIterator m_begin;
  VertexIterator m_curr;
  VertexIterator m_end;
  //device_ptr<Graph>& m_g;
  Graph* m_g;
  thrust::pair<OutEdgeIterator,OutEdgeIterator> m_edges;
  
public:
  typedef std::forward_iterator_tag iterator_category;
  typedef typename OutEdgeIterator::value_type      value_type;
  typedef typename OutEdgeIterator::reference       reference;
  typedef typename OutEdgeIterator::pointer         pointer;
  typedef typename OutEdgeIterator::difference_type difference_type;
  typedef difference_type distance_type;
  
  __host__ __device__
  inline adj_list_edge_iterator()
    : m_g(0) {}
  
  /*__host__ __device__
  inline adj_list_edge_iterator( const self_t& x)
    : m_begin(x.m_begin), m_curr(x.m_curr), m_end(x.m_end)
    , m_edges(x.m_edges), m_g(x.m_g) {}*/
  
  __host__ __device__
  inline adj_list_edge_iterator( VertexIterator b,
                                 VertexIterator c, 
                                 VertexIterator e, 
                                 Graph& g)
    : m_begin(b), m_curr(c), m_end(e), m_g(&g)
  {
    if ( m_curr != m_end ) {
      while ( m_curr != m_end && out_degree( *m_curr, *m_g, called_space()) == 0 )
        ++m_curr;
      if ( m_curr != m_end )
        m_edges = out_edges( *m_curr, *m_g, called_space());
    }
  }
  
public:
  __host__ __device__
  self_t& operator++()
  {
    ++m_edges.first;
    if( m_edges.first ==m_edges.second){
      ++m_curr;
      while ( m_curr != m_end && out_degree( *m_curr, *m_g, called_space()) == 0 )
        ++m_curr;
      if ( m_curr != m_end )
        m_edges = out_edges( *m_curr, *m_g, called_space());
    }
    return *this;
  }
  
  __host__ __device__
  self_t& operator=( const self_t& x){
    m_begin =x.m_begin;
    m_curr  =x.m_curr;
    m_end   =x.m_end;
    m_edges =x.m_edges;
    m_g =x.m_g;
    return *this;
  }
  
  __host__ __device__
  inline bool operator==( const self_t& x) const {
    return m_curr ==x.m_curr
      && ( m_curr ==m_end || m_edges.first ==x.m_edges.first);
  }
  
  __host__ __device__
  inline bool operator!=( const self_t& x) const {
    return m_curr !=x.m_curr 
      || ( m_curr !=m_end  && m_edges.first !=x.m_edges.first);
  }
  
  __host__ __device__
  inline value_type operator*() const { 
    return *m_edges.first;
  }
  
};

} // end namespace detail

} // end namespace thrust
